在前一篇(Day 28)我們整合了 RBAC + ABAC,今天要更進一步,將 Scope(範圍控制) 也引入授權邏輯,並討論一些專案中的最佳實務做法。
今天的案例同時考慮三個層面:
ROLE_ADMIN
。FINANCE
。read
或 write
。這三者加在一起,就能實現更精細的授權策略。
// RBAC: 只有 ROLE_ADMIN 可進
@GetMapping("/admin")
@PreAuthorize("hasAuthority('ROLE_ADMIN')")
public String adminOnly() {
return "這是管理員專區 (RBAC)";
}
// ABAC + Scope + Role: 三重檢查
@GetMapping("/finance/report")
@PreAuthorize("hasAuthority('ROLE_ADMIN') and @scopeSecurity.hasScope(authentication, 'read') and @departmentSecurity.checkDepartment(authentication, 'FINANCE')")
public String financeReport() {
return "這是財務報表,只有 Finance 部門 + Admin + 有 read scope 才能看 (Scope+Role+Attribute)";
}
/admin
→ 單純 RBAC。/finance/report
→ 必須同時符合 角色 + Scope + 部門。String role = claims.get("role", String.class);
String scope = claims.get("scope", String.class);
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(
username,
null,
List.of(new SimpleGrantedAuthority(role))
);
authentication.setDetails(Map.of("scope", scope));
SecurityContextHolder.getContext().setAuthentication(authentication);
scope
。scope
放進 authentication.setDetails()
,讓後續的授權判斷可以讀取。public static String generateAccessToken(String username, String role, String scope) {
return Jwts.builder()
.setSubject(username)
.addClaims(Map.of("role", role, "scope", scope))
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 60 * 1000)) // 1 分鐘
.signWith(key, SignatureAlgorithm.HS256)
.compact();
}
ROLE_ADMIN
+ read write
。ROLE_USER
+ read
。package com.ansathsean.security;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
@Component("scopeSecurity")
public class ScopeSecurity {
public boolean hasScope(Authentication authentication, String requiredScope) {
Object details = authentication.getDetails();
if (details instanceof java.util.Map<?, ?> map) {
String scopes = (String) map.get("scope");
if (scopes != null) {
for (String s : scopes.split(" ")) {
if (s.equals(requiredScope)) {
return true;
}
}
}
}
return false;
}
}
read
。@PreAuthorize
中靈活使用。read
,不該擁有 write
。role
:放置在 JWT claims。scope
:以空白分隔的字串,方便解析。attribute
:如 department
,可以來自 DB 或 OIDC Claim。/finance/report
。到了這裡,我們完成了 進階授權策略,將 Role + Scope + Attribute 三者結合,實現真正靈活的存取控制。
這樣的設計更貼近真實世界的需求,例如「某部門主管才能審核報表」、「特定 API 僅能在工作時段使用」。這也代表我們將全部的授權與認證的知識都交給大家了!
感謝大家的觀看,不知不覺也已經過了29天,相信大家對於授權與認證的情境了解得更多,我自己在開發的過程中也學到了很多,身為一個工程師,學到最多東西的時段絕對不是開發(畢竟現在有AI在,開發輕鬆很多),最重要的還是在測試的過程中,在試誤的過程中學到更多扎實的內容。希望我學到的這些知識能夠成為大家的養分~
明天就是最後一天,我會整理 Spring Security 實戰 30 天學習總結,寫一些我的心得,非常感謝大家的收看,那我們,明天見!